#pragma once

#include <memory>
#include "Containers/Containers.h"
#include "ScriptCompiler/Description/IdentifierDescription.h"

#include "ScriptCompiler/Description/FieldDescription.h"
#include "ScriptCompiler/Description/PropertyDescription.h"
#include "ScriptCompiler/Description/FunctionDescription.h"

#include "ScriptRuntime/ScriptInnerType.h"

#include "ScriptCompiler/AST/ClassNode.h"
#include "ScriptCompiler/AST/EnumNode.h"
#include "ScriptCompiler/Description/GenericTypeArguments.h"
#include "ScriptCompiler/Description/ClassAlias.h"
#include "DecoratedTypeDescription.h"

class CGSharpFieldDescription;
class CPropertyDescription;
class CFunctionDescription;
class CDecoratorDescription;
class CConstructorDescription;
class CNamespaceDescription;
class CScriptFileContext;


class CTypeDescription : public IIdentifierDescription
{
    
public:
    CTypeDescription()
    {
    }

    virtual EIdentifierType GetIdentifierType() const override
    {
        return EIdentifierType::Class;
    }

    //parent scope of this type
    //might be a type or a namespace
    struct STypeParentScope
    {
        
        STypeParentScope()=default;

        STypeParentScope(std::shared_ptr<CTypeDescription> InParentType)
            :Type(InParentType)
        {
        }

        STypeParentScope(std::shared_ptr<CNamespaceDescription> InParentNamespace)
            :Namespace(InParentNamespace)
        {
        }

        std::weak_ptr<CTypeDescription> Type;
        std::weak_ptr<CNamespaceDescription> Namespace;
    };

    //not valid for generic type parameter
    STypeParentScope ParentScope;
    //not valid for generic type parameter
    uint32_t IndexInParent = 0;

    //这个类所在的命名范围链,从外到内
    //特化的类型没有父命名范围，这个数组为空
    TVector<CString> ParentScopeNameChain;

    //这个类型的名称，不包含命名空间
    CString Name;
    
    //获取完整名称,特化的类型没有命名空间
    //命名空间::命名空间::类名
    CString GetFullName() const;

    //if this is a class, the Type ID is ID of SharedObjectPtr<T> type
    STypeID TypeID;


    bool IsScriptType() const
    {
        return !IsNative();
    }

//for normal object

    //if this is a class, the Type ID is ID of SharedObjectPtr<T> type
    //and the RawClassTypeID is ID of T type
    TOptional<STypeID> RawClassTypeID;

//for place holder:

    //index for generic type parameter
    uint32_t GenericPlaceholderIndex = 0;
    //class/function has this generic type parameter
    TVariant<std::weak_ptr<CTypeDescription>,std::weak_ptr<CFunctionDescription>> GenericPlaceholderParent;
    //this placeholder present a type list,such as Args... in FunctionType<ReturnType(Args...)>
    bool bIsListPlaceholder=false;

//for template class:
    //all generallized classes
    TMap<SGenericTypeArgumentGroup, std::shared_ptr<CTypeDescription>> GeneralizedClasses;

    //generic type parameters
    TVector<std::shared_ptr<CTypeDescription>> GenericPlaceholders;

//for generalized class:
    SGenericTypeArgumentGroup GenericArguments;
    //template class 
    std::shared_ptr<CTypeDescription> GenericClass;

    bool IsGenerailizedClass() const
    {
        return GenericClass!=nullptr;
    }

//for delegate
    // SDecoratedTypeDescription GetDelegateReturnType() const;
    // TVector<std::shared_ptr<CParameterDescription>> DelegateParameters;
    std::shared_ptr<CDelegateNode> DelegateNode;
    SDecoratedTypeDescription DelegateFunctionPointerType;

    //get the constructor that can construct delegate from a function pointer
    std::shared_ptr<CConstructorDescription> GetDelegateConstructorByFunctionPointer();
    //the invoke function of delegate
    std::shared_ptr<CFunctionDescription> GetDelegateInvokeFunction();
    //the function that bind a functionpointer 
    std::shared_ptr<CFunctionDescription> GetDelegateBindFunctionPointerFunction();
    //the function that unbind a functionpointer
    std::shared_ptr<CFunctionDescription> GetDelegateUnbindFunctionPointerFunction();
    //the static function that + two delegate
    std::shared_ptr<CFunctionDescription> GetDelegateAppendFunction();
    //the static function that - two delegate
    std::shared_ptr<CFunctionDescription> GetDelegateRemoveFunction();
    //the function that clear all bindings
    std::shared_ptr<CFunctionDescription> GetDelegateClearFunction();

//for native type
    bool IsNative() const
    {
        return bIsNative;
    }
    bool bIsNative=false;

//for function pointer
    SDecoratedTypeDescription FunctionPointerReturnType;
    TVector<SDecoratedTypeDescription> FunctionPointerParameters;

//for normal class:
    std::shared_ptr<CClassNode> ClassNode;

//for enum 
    std::shared_ptr<CEnumNode> EnumNode;
    SDecoratedTypeDescription EnumValueType;

//common part:
    //base class
    std::shared_ptr<CTypeDescription> BaseClass;

    TVector<std::shared_ptr<CTypeDescription>> BaseInterfaces;

    std::shared_ptr<CDecoratorDescription> Decorator;

    TVector<std::shared_ptr<CTypeDescription>> Classes;

    //the class that is used in this class , but not declared in this class
    TVector<std::shared_ptr<CClassAlias>> UsingClasses;

    //the alias of this class
    TVector<std::weak_ptr<CClassAlias>> Alias;

    //TVector<std::shared_ptr<CTypeDescription>> Enums;

    TVector<std::shared_ptr<CGSharpFieldDescription>> Fields;

    //index for first sub class non-static field
    //only valid for native class 
    //do not consider static field , because native field is not in script class now
    int32_t SubclassNonStaticFieldIndexStart=-1;

    TVector<std::shared_ptr<CPropertyDescription>> Properties;
    
    TVector<std::shared_ptr<CPropertyDescription>> Indexers;

    TVector<std::shared_ptr<CFunctionDescription>> Functions;

    TVector<std::shared_ptr<CConstructorDescription>> Constructors;

    virtual CString GetSearchName() const override
    {
        return Name;
    }

    enum class EClassType
    {
        InnerType,
        Class, 
        Interface,
        Struct,
        Enum,
        Delegate,
        FunctionPointer,
        GenericPlaceholder
    };

    EClassType GetClassType() const
    {
        return ClassType;
    }

    void SetClassType(EClassType InClassType)
    {
        ClassType=InClassType;
    }

    void MarkAsInterface()
    {
        ClassType=EClassType::Interface;
    }

    void MarkAsStruct()
    {
        ClassType=EClassType::Struct;
    }

    void MarkAsClass()
    {
        ClassType=EClassType::Class;
    }

    void MarkAsEnum()
    {
        ClassType=EClassType::Enum;
    }

    void MarkAsInnerType()
    {
        ClassType=EClassType::InnerType;
    }
    
    bool MarkAsGenericPlaceholder()
    {
        ClassType=EClassType::GenericPlaceholder;
        return true;
    }

    void MarkAsDelegate()
    {
        ClassType=EClassType::Delegate;
    }

    void MarkAsFunctionPointer()
    {
        ClassType=EClassType::FunctionPointer;
    }

    bool IsInterface() const
    {
        return ClassType==EClassType::Interface;
    }

    bool IsStruct() const
    {
        return ClassType==EClassType::Struct;
    }

    bool IsClass() const
    {
        return ClassType==EClassType::Class;
    }

    bool IsEnum() const
    {
        return ClassType==EClassType::Enum;
    }

    bool IsInnerType() const
    {
        return ClassType==EClassType::InnerType;
    }

    bool IsNullObject() const
    {
        if(InnerType.HasValue())
        {
            return InnerType.GetValue()==EScriptInnerType::NullObject;
        }
        return false;
    }

    bool IsNullPtr() const
    {
        if(InnerType.HasValue())
        {
            return InnerType.GetValue()==EScriptInnerType::NullPtr;
        }
        return false;
    }   

    bool IsDelegate() const
    {
        return ClassType==EClassType::Delegate;
    }

    bool IsFunctionPointer() const
    {
        return ClassType==EClassType::FunctionPointer;
    }

    bool IsGenericPlaceholder() const
    {
        return ClassType==EClassType::GenericPlaceholder;
    }

    bool IsValueType()
    {
        return !IsClass();
    }

    bool IsGeneric() const
    {
        return GenericPlaceholders.Num() > 0;
    }

    bool IsA(const std::shared_ptr<CTypeDescription>& InClass) const
    {
        if (InClass == nullptr)
        {
            return false;
        }

        if (InClass->TypeID == TypeID)
        {
            return true;
        }

        if (BaseClass)
        {
            return BaseClass->IsA(InClass);
        }

        return false;
    }



protected:  

    EClassType ClassType= EClassType::Class;

public:
    //find function by name
    TVector<std::shared_ptr<CFunctionDescription>> FindFunctions(const CString & InFunctionName)
    {
        TVector<std::shared_ptr<CFunctionDescription>> Result;
        for (auto& Function : Functions)
        {
            if (Function->GetSearchName() == InFunctionName)
            {
                Result.Add(Function);
            }
        }
        
        if(BaseClass)
        {
            auto ParentResult=BaseClass->FindFunctions(InFunctionName);
            Result.Append(ParentResult);
        }

        return Result;
    }

    bool IsPosibleFunction(const CString & InFunctionName)
    {
        for (auto& Function : Functions)
        {
            if (Function->GetSearchName() == InFunctionName)
            {
                return true;
            }
        }

        if(BaseClass)
        {
            return BaseClass->IsPosibleFunction(InFunctionName);
        }

        return false;
    }

    //find function by name and type
    std::shared_ptr<CFunctionDescription> FindFunctionByNameAndType(const CString & InFunctionName, SDecoratedTypeDescription InFunctionType);

    //find property by name
    std::shared_ptr<CPropertyDescription> FindProperty(const CString & InPropertyName)
    {
        for (auto& Property : Properties)
        {
            if (Property->GetSearchName() == InPropertyName)
            {
                return Property;
            }
        }

        if(BaseClass)
        {
            return BaseClass->FindProperty(InPropertyName);
        }

        return nullptr;
    }

    //find field by name
    std::shared_ptr<CGSharpFieldDescription> FindFieldByName(const CString & InFieldName)
    {
        for (auto& Field : Fields)
        {
            if (Field->GetSearchName() == InFieldName)
            {
                return Field;
            }
        }

        if(BaseClass)
        {
            return BaseClass->FindFieldByName(InFieldName);
        }

        return nullptr;
    }

    //find enum by name
    // std::shared_ptr<CTypeDescription> FindEnum(const CString & InEnumName)
    // {
    //     for (auto& Enum : Enums)
    //     {
    //         if (Enum->GetSearchName() == InEnumName)
    //         {
    //             return Enum;
    //         }
    //     }
    //     return nullptr;
    // }

    //find subclass by name
    std::shared_ptr<CTypeDescription> FindClass(const CString & InClassName)
    {
        for (auto& Class : Classes)
        {
            if (Class->Name == InClassName)
            {
                return Class;
            }
        }

        //find in generic placeholders
        for(auto& GenericPlaceholder:GenericPlaceholders)
        {
            if(GenericPlaceholder->GetSearchName()==InClassName)
            {
                return GenericPlaceholder;
            }
        }
        
        for(auto& UsingClass : UsingClasses)
        {
            if(UsingClass->AliasName == InClassName)
            {
                return UsingClass->Class;
            }
        }

        return nullptr;
    }

    //find a class in file context, the class is not nessasery sub class
    std::shared_ptr<CTypeDescription> FindClassInFile(
        std::shared_ptr<CScriptFileContext> InFile
        ,const CString & InClassName
    );

    std::shared_ptr<CNamespaceDescription> FindNamespaceInFile(
        std::shared_ptr<CScriptFileContext> InFile
        ,const CString & InNamespaceName
    );
    
    TOptional<EScriptInnerType> InnerType;


public:
    bool bVirtualFunctionTableInitialized = false;
    
    TVector<std::shared_ptr<CFunctionDescription>> VirtualFunctionTable;

};